﻿// Copyright (c) 2002-2003, Sony Computer Entertainment America
// Copyright (c) 2002-2003, Craig Reynolds <craig_reynolds@playstation.sony.com>
// Copyright (C) 2007 Bjoern Graf <bjoern.graf@gmx.net>
// Copyright (C) 2007 Michael Coles <michael@digini.com>
// Copyright (C) 2010 Thiago Dias Pastor <thiagodiaspastor@gmail.com>
// All rights reserved.

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using PloobsEngine.Cameras;

namespace PloobsEngine.Draw
{

    internal class DeferredText
    {
        public static void SetProperties(EngineStuff eng,SpriteFont f)
        {
            engine = eng;
            font = f;
        }

        public static void SetProperties(SpriteFont f)
        {         
            font = f;
        }

        public static void SetProperties(EngineStuff eng)
        {
            engine = eng;
            font = EngineStuff.InternalContentManager.GetAsset<SpriteFont>("ConsoleFont");
        }        

        static EngineStuff engine;
        static SpriteFont font;

        internal static void setClearEachFrame(bool shouldClear)
        {
            clear = shouldClear;
        }

        internal static void Clear()
        {
            texts.Clear();
        }

        public static void DrawAll()
        {
            EngineStuff.SpriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);

            foreach (TextEntry text in texts)
            {
                EngineStuff.SpriteBatch.DrawString(font, text.Text, text.Position, text.Color);
            }
            if (clear)
                texts.Clear();

            EngineStuff.SpriteBatch.End();

        }

        static List<TextEntry> texts = new List<TextEntry>();
        static bool clear = true;
        public static void AddToBuffer(TextEntry text)
        {
            texts.Add(text);
        }

    }



    internal class DeferredLine
    {
        static DeferredLine()
        {
            deferredLineArray = new DeferredLine[size];
            for (int i = 0; i < size; i++)
            {
                deferredLineArray[i] = new DeferredLine();
            }
        }

        public static void AddToBuffer(Vector3 s, Vector3 e, Color c)
        {
            if (index < size)
            {
                deferredLineArray[index].startPoint = s;
                deferredLineArray[index].endPoint = e;
                deferredLineArray[index].color = c;
                index++;
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("overflow in deferredDrawLine buffer");
            }
        }

            internal static void Clear()
            {
               index = 0;
            }      

        public static void DrawAll()
        {
            // draw all lines in the buffer
            for (int i = 0; i < index; i++)
            {
                DeferredLine dl = deferredLineArray[i];
                Drawing.iDrawLine(dl.startPoint, dl.endPoint, dl.color);
            }

            // reset buffer index
            if(clear)
           index = 0;
        }

        internal static void setClearEachFrame(bool shouldClear)
        {
            clear = shouldClear;
        }

        Vector3 startPoint;
        Vector3 endPoint;
        Color color;

        static int index = 0;
        const int size = 3000;
        static bool clear = true;
        static DeferredLine[] deferredLineArray;
    }

    internal class DeferredCircle
    {
        static DeferredCircle()
        {
            deferredCircleArray = new DeferredCircle[size];
            for (int i = 0; i < size; i++)
            {
                deferredCircleArray[i] = new DeferredCircle();
            }
        }

        internal static void Clear()
        {
            index = 0;
        }      


        public static void AddToBuffer(float radius, Vector3 axis, Vector3 center, Color color, int segments, bool filled, bool in3d)
        {
            if (index < size)
            {
                deferredCircleArray[index].radius = radius;
                deferredCircleArray[index].axis = axis;
                deferredCircleArray[index].center = center;
                deferredCircleArray[index].color = color;
                deferredCircleArray[index].segments = segments;
                deferredCircleArray[index].filled = filled;
                deferredCircleArray[index].in3d = in3d;
                index++;
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("overflow in deferredDrawCircle buffer");
            }
        }

        internal static void setClearEachFrame(bool shouldClear)
        {
            clear = shouldClear;
        }

        public static void DrawAll()
        {
            // draw all circles in the buffer
            for (int i = 0; i < index; i++)
            {
                DeferredCircle dc = deferredCircleArray[i];
                Drawing.DrawCircleOrDisk(dc.radius, dc.axis, dc.center, dc.color, dc.segments, dc.filled, dc.in3d);
            }

            // reset buffer index
            if(clear)
            index = 0;
        }

        static bool clear = true;
        float radius;
        Vector3 axis;
        Vector3 center;
        Color color;
        int segments;
        bool filled;
        bool in3d;

        static int index = 0;
        const int size = 500;
        static DeferredCircle[] deferredCircleArray;
    }

    /// <summary>
    /// Represents a box in three dimensional space.
    /// </summary>
    internal class Box
    {
        /// <summary>
        /// The vertices that represent a box.
        /// </summary>
        private static readonly Vector3[] Vertices =
            {
                new Vector3(-1f, 1f, -1f), new Vector3(1f, 1f, -1f),
                new Vector3(1f, 1f, 1f), new Vector3(-1f, 1f, 1f),
                new Vector3(-1f, -1f, -1f), new Vector3(1f, -1f, -1f),
                new Vector3(1f, -1f, 1f), new Vector3(-1f, -1f, 1f)
            };

        /// <summary>
        /// The indices used to draw a box's vertices.
        /// </summary>
        private static readonly short[] Indices =
            {
                0, 1, 1, 2, 2, 3, 3, 0,
                4, 5, 5, 6, 6, 7, 7, 4,
                0, 4, 1, 5, 2, 6, 3, 7
            };

        /// <summary>
        /// Information about a box's vertices.
        /// </summary>
        private static readonly VertexElement[] VertexElements =
            {
                new VertexElement(0, 0, VertexElementFormat.Vector3,
                    VertexElementMethod.Default, VertexElementUsage.Position, 0)
            };

        /// <summary>
        /// The graphics device used to draw boxes.
        /// </summary>
        private static GraphicsDevice graphicsDevice = null;

        /// <summary>
        /// Gets or sets the graphics device used to draw boxes.
        /// </summary>
        private static GraphicsDevice GraphicsDevice
        {
            get { return Box.graphicsDevice; }
            set
            {
                if (Box.graphicsDevice == null)
                {
                    Box.graphicsDevice = value;
                    Box.vertexDeclaration = new VertexDeclaration(Box.GraphicsDevice, Box.VertexElements);
                    Box.effect = new BasicEffect(Box.GraphicsDevice, null);
                }
            }
        }

        /// <summary>
        /// Information about a box's vertices.
        /// </summary>
        private static VertexDeclaration vertexDeclaration = null;

        /// <summary>
        /// The basic effect used to draw boxes.
        /// </summary>
        private static BasicEffect effect = null;

        /// <summary>
        /// Creates a new box.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device used to draw the box.</param>
        /// <param name="color">The box's color.</param>
        /// <param name="center">The box's center coordinates.</param>
        /// <param name="radius">The box's radius.</param>
        public Box()
        {
            Box.GraphicsDevice = EngineStuff.GraphicsDevice;
        }

        /// <summary>
        /// Draws the box.
        /// </summary>
        /// <param name="view">The viewing matrix.</param>
        /// <param name="projection">The projection matrix.</param>
        public void Draw(Matrix view, Matrix projection, Vector3 center, float radius, Color color)
        {
            if (Box.GraphicsDevice != null)
            {
                // Setup the graphics device.
                Box.GraphicsDevice.VertexDeclaration = Box.vertexDeclaration;
                Box.GraphicsDevice.RenderState.DepthBufferEnable = true;
                Box.GraphicsDevice.RenderState.AlphaBlendEnable = false;
                Box.GraphicsDevice.RenderState.AlphaTestEnable = false;

                // Setup the effect.
                Box.effect.DiffuseColor = color.ToVector3();
                Box.effect.World = Matrix.CreateScale(radius) *
                    Matrix.CreateTranslation(center);
                Box.effect.View = view;
                Box.effect.Projection = projection;

                // Apply the effect to the box.
                Box.effect.Begin();

                // Apply the effect passes to the box.
                foreach (EffectPass pass in Box.effect.CurrentTechnique.Passes)
                {
                    // Draw the box.
                    pass.Begin();
                    Box.GraphicsDevice.DrawUserIndexedPrimitives<Vector3>
                        (PrimitiveType.LineList, Box.Vertices, 0, Box.Vertices.Length,
                        Box.Indices, 0, Box.Indices.Length / 2);
                    pass.End();
                }

                Box.effect.End();
            }
        }

    }

    internal struct BoxData
    {
        public Vector3 Position;
        public float Radius;
        public Color color;
    }


    internal class DeferredBox
    {
        static List<BoxData> Boxes = new List<BoxData>();        
        static Box box = new Box();        

        public static void AddBox(Color color, Vector3 center, float radius)
        {
            BoxData b = new BoxData();
            b.Radius = radius;
            b.Position = center;
            b.color = color;
            Boxes.Add(b);
        }

        internal static void Clear()
        {
            Boxes.Clear();
        }

        public static void DrawAll(ICamera cam)
        {            
            foreach (var item in Boxes)
            {
                box.Draw(cam.View, cam.Projection, item.Position, item.Radius, item.color);
            }
            
            if (clear)
                Boxes.Clear();
        }

        internal static void setClearEachFrame(bool shouldClear)
        {
            clear = shouldClear;
        }

        static bool clear;
    }

}
